package com.louis.json;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;
import org.joda.time.DateTime;

import com.louis.annotation.Json;
import com.louis.util.ArrayUtil;
import com.louis.util.FieldUtil;
import com.louis.util.MethodUtil;
import com.louis.util.ResourceBundleUtil;
import com.louis.util.StringUtil;

/**
 * java对象和json之间的工具
 * 
 * @author louisjiang
 * 
 */
public class JsonViewer {
	private final static Logger logger = Logger.getLogger(JsonViewer.class);
	private final static String N = "\n";
	private final static String T = "\t";
	private final static String LOCAL_PROPERTIES = "com/louis/json/JsonViewer";
	private final static String USER_PROPERTIES = "JsonViewer";
	private static JsonViewer jsonViewer;
	private SimpleDateFormat simpleDateFormat = null;
	private int count = 1;
	/**
	 * 需要被格式化的类型
	 */
	private Map<String, String> formatClasses;
	/**
	 * 基本类型,这种类型将不会进一步解析
	 */
	private List<String> baseClasses;

	private JsonViewer() {
		init();
	}

	private void init() {
		if (logger.isDebugEnabled()) {
			logger.debug("Enter into the init method.");
		}
		formatClasses = ResourceBundleUtil.getMap(LOCAL_PROPERTIES, "formatClasses");
		formatClasses.putAll(ResourceBundleUtil.getMap(USER_PROPERTIES, "formatClasses"));
		baseClasses = ResourceBundleUtil.getList(LOCAL_PROPERTIES, "baseClasses");
		List<String> list = ResourceBundleUtil.getList(USER_PROPERTIES, "baseClasses");
		for (String value : list) {
			if (!baseClasses.contains(value)) {
				baseClasses.add(value);
			}
		}
	}

	public static JsonViewer getInstance() {
		if (jsonViewer == null) {
			jsonViewer = new JsonViewer();
		}
		return jsonViewer;
	}

	public String toJson(List<?> list, boolean format, Json json) {
		StringBuilder jsonStringBuilder = new StringBuilder("[");
		int size = list.size();
		for (int i = 0; i < size; i++) {
			Object object = list.get(i);
			String jsonString = toJson(object, format, json);
			if (jsonString != null && !jsonString.equals("")) {
				jsonStringBuilder.append(jsonString);
				if (i < size - 1) {
					jsonStringBuilder.append(",");
					
				}
			}
		}
		jsonStringBuilder.append("]");
		return jsonStringBuilder.toString();
	}

	public String toJson(List<?> list, boolean format) {
		return toJson(list, format, null);
	}

	public String toJson(Object object, boolean format) {
		return toJson(object, format, null);
	}

	private String toJson(Object object, boolean format, Json json) {
		StringBuilder jsonStringBuilder = new StringBuilder();
		Class<?> clazz = object.getClass();
		if (json == null) {
			json = clazz.getAnnotation(Json.class);
		}
		List<String> fieldNames = getFieldNames(clazz, json);
		boolean nullField = true;
		if (json != null) {
			nullField = json.nullField();
		}
		if (!fieldNames.isEmpty()) {
			int size = fieldNames.size();
			for (int i = 0; i < size; i++) {
				String fieldName = fieldNames.get(i);
				Method method = MethodUtil.getGetterMethod(clazz, fieldName);
				if (method != null) {
					Object returnValue = MethodUtil.invoke(object, method);
					if (returnValue != null) {
						Class<?> returnClass = returnValue.getClass();
						String className = returnClass.getName();
						if (baseClasses.contains(className)) {
							if (className.equals("java.lang.String")) {
								returnValue = "'" + returnValue + "'";
							}
						} else {
							Set<String> keys = formatClasses.keySet();
							if (keys.contains(className)) {
								String pattern = formatClasses.get(className);
								if (className.equals("org.joda.time.DateTime")) {
									returnValue = ((DateTime) returnValue).toString(pattern);
								} else if (className.equals("java.util.Date")) {
									simpleDateFormat = new SimpleDateFormat(pattern);
									returnValue = simpleDateFormat.format(returnValue);
								}
								returnValue = "'" + returnValue + "'";
							} else {
								Class<?>[] continueClasses = returnClass.getInterfaces();
								boolean isList = false;
								for (Class<?> continueClass : continueClasses) {
									if (continueClass.equals(List.class)) {
										isList = true;
										break;
									}
								}
								count++;
								Field field = FieldUtil.getDeclaredField(clazz, fieldName);
								Json fieldJson = field.getAnnotation(Json.class);
								if (isList) {
									returnValue = toJson((List<?>) returnValue, format, fieldJson);
								} else {
									returnValue = toJson(returnValue, format, fieldJson);
								}
								count--;
							}
						}
					}
					if (returnValue == null && !nullField) {
						continue;
					}
					if (i > 0 && i < size) {
						jsonStringBuilder.append(",");
					}
					if (format) {
						jsonStringBuilder.append(N);
						for (int j = 0; j < count; j++) {
							jsonStringBuilder.append(T);
						}
					}
					jsonStringBuilder.append(fieldName + ":" + returnValue);
				}
			}
			if (format) {
				jsonStringBuilder.append(N);
				for (int j = 1; j < count; j++) {
					jsonStringBuilder.append(T);
				}
			}
		}
		if (StringUtil.hasText(jsonStringBuilder.toString())) {
			jsonStringBuilder.insert(0, "{");
			jsonStringBuilder.append("}");
		}
		return jsonStringBuilder.toString();
	}

	private List<String> getFieldNames(Class<?> clazz, Json json) {
		boolean transientField = true;
		String[] excludeFields = null;
		String[] includeFields = null;
		if (json != null) {
			transientField = json.transientField();
			excludeFields = json.excludeFields();
			includeFields = json.includeFields();
		}
		List<String> list = new ArrayList<String>();
		if (includeFields != null && includeFields.length > 0) {
			for (String includeField : includeFields) {
				Field field = FieldUtil.getDeclaredField(clazz, includeField);
				if (Modifier.isTransient(field.getModifiers()) && !transientField) {
					continue;
				}
				list.add(field.getName());
			}
		} else {
			Collection<Field> fields = FieldUtil.getDeclaredFields(clazz, true);
			boolean flag = true;
			for (Field field : fields) {
				String name = field.getName();
				if (name.equals("serialVersionUID")) {
					continue;
				}
				if (Modifier.isTransient(field.getModifiers()) && !transientField) {
					continue;
				}
				if (ArrayUtil.hasArray(excludeFields)) {
					flag = true;
					for (String excludeField : excludeFields) {
						if (excludeField.equals(name)) {
							flag = false;
							break;
						}
					}
				}
				if (flag) {
					list.add(name);
				}
			}
		}
		return list;
	}

	public static void main(String[] args) {
		try {
			java.lang.Runtime.getRuntime().exec("C:\\Documents and Settings\\louisjiang\\Desktop\\SVN.txt");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
